package cn.com.infcn.spider;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.redisson.core.RQueue;
import org.redisson.core.RSet;

import com.alibaba.fastjson.JSONObject;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;

import cn.com.infcn.spider.domain.HttpJob;
import cn.com.infcn.spider.domain.Job;
import cn.com.infcn.spider.domain.Page;
import cn.com.infcn.spider.domain.UriInfo;
import cn.com.infcn.spider.domain.UrlInfo;
import cn.com.infcn.spider.util.MongoDao;
import cn.com.infcn.spider.util.RedisDao;
import cn.com.infcn.spider.util.StaticValue;

public class URIManager {

	private static RedisDao redisDao = StaticValue.getBean(RedisDao.class, "redisDao");

	private static MongoDao mongoDao = StaticValue.getBean(MongoDao.class, "mongoDao");

	private static final Map<String, RQueue<UriInfo>> URI_MAP = new HashMap<>();

	private static final Map<String, RSet<String>> URI_SET = new HashMap<>();

	public static final String SUCCESS = "SUCCESS_";

	public static final String ERROR = "ERR_";

	/**
	 * 增加一个网址到任务队列
	 * 
	 * @param job
	 * @param uri
	 * @param check
	 */
	public static void add(Job job, UrlInfo uri) {
		add(job, uri, true);
	}

	public static synchronized void add(Job job, UriInfo uriInfo, boolean check) {
		String jobName = job.getName();

		if (check && checkUrl(jobName, uriInfo.getUri())) {
			return;
		}
		uriSet(jobName).add(uriInfo.getUri());
		uriQueue(jobName).add(uriInfo);
	}

	public static boolean checkUrl(String jobName, String uri) {
		return uriSet(jobName).contains(uri) || mongoDao.check(SUCCESS + jobName, uri) || mongoDao.check(ERROR + jobName, uri);
	}

	/**
	 * 获取一个网址
	 * 
	 * @param job
	 * @return
	 */
	public static UriInfo pop(Job job) {
		return uriQueue(job.getName()).poll();
	}

	public static RQueue<UriInfo> uriQueue(String name) {
		RQueue<UriInfo> queue = URI_MAP.get(name);
		if (queue != null) {
			return queue;
		}
		synchronized (URI_MAP) {
			queue = URI_MAP.get(name);
			if (queue != null) {
				return queue;
			}
			queue = redisDao.getRedisson().getQueue(name);
			URI_MAP.put(name, queue);
			return queue;
		}
	}

	private static RSet<String> uriSet(String name) {
		RSet<String> set = URI_SET.get(name);
		if (set != null) {
			return set;
		}
		synchronized (URI_SET) {
			set = URI_SET.get(name);
			if (set != null) {
				return set;
			}
			set = redisDao.getRedisson().getSet("SET_" + name);
			set.expire(20, TimeUnit.MINUTES);
		}
		return set;
	}

	/**
	 * 彻底删除一个job
	 * 
	 * @param job
	 */
	public static void drop(String jobName) {
		URI_MAP.remove(jobName);
		URI_SET.remove("SET_"+jobName);
		uriSet(jobName).delete();
		uriQueue(jobName).delete();
		mongoDao.getCollection(URIManager.ERROR + jobName).drop();
		mongoDao.getCollection(URIManager.SUCCESS + jobName).drop();
	}

	/**
	 * 保存对象到 数据库用来记录成功
	 * 
	 * @param job
	 * @param page
	 */
	public static void addPage(HttpJob job, Page page, UrlInfo urlInfo) {
		String jobName = job.getName();
		JSONObject result = (JSONObject) JSONObject.toJSON(urlInfo);
		result.remove("url");
		result.put("_id", page.getUrl());
		result.put("update_time", new Date());
		if (page.isOk()) {
			if (job.isSaveHtml()) {
				result.put("html", page.getHtml());
			}
			mongoDao.save(SUCCESS + jobName, result);
		} else {
			result.put("errCount", urlInfo.getErrCount() + 1);
			mongoDao.save(ERROR + jobName, result);
		}

	}

	public static void changeStatus(String jobName, int status) {
		DBObject job = mongoDao.findById("job", jobName);
		job.put("status", status);
		job.put("updateTime", new Date());
		mongoDao.save("job", job);
	}

	/**
	 * 重置错误到队列
	 * 
	 * @param name
	 */
	public static synchronized long resetErr(Job job) {

		String tableName = URIManager.ERROR + job.getName();

		DBCollection collection = mongoDao.getCollection(tableName);

		long count = collection.count();

		if (count > 0) {
			try (DBCursor find = collection.find()) {
				for (DBObject obj : find) {
					URIManager.add(job, new UrlInfo(obj), false);
					mongoDao.remove(tableName, (DBObject) new BasicDBObject("_id", obj.get("_id")));
				}
			}
		}

		return count;

	}

}
